package org.visionlibrary.image.geomtric.model;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.visionlibrary.image.geomtric.util.Debug;
import org.visionlibrary.image.geomtric.util.Utils;



import com.thoughtworks.xstream.XStream;

public class TShapes {
	public static void generateRescale(List<TShape> shapes, double step,
			double max) {
		List<TShape> rescaled = new ArrayList<TShape>();
		for (int i = 0; i < shapes.size(); i++) {
			int k = 0;
			for (double j = step; j <= max; j += step) {
				double rounded = Utils.roundDouble(j, 2);
				TShape scaled = shapes.get(i).copy(
						shapes.get(i).getName() + "_resc_" + rounded);
				scaled.scale(rounded);
				rescaled.add(scaled);
				k++;
			}
			shapes.addAll(i + 1, rescaled);
			i += k;
			rescaled.clear();
		}
	}

	public static void generateRotation(List<TShape> shapes, int step, int max) {
		List<TShape> rotated = new ArrayList<TShape>();
		for (int i = 0; i < shapes.size(); i++) {
			int k = 0;
			for (int j = step; j <= max; j += step) {
				double rounded = Utils.roundDouble(j, 2);
				double angleInRad = Math.toRadians(rounded);
				TShape rotate = shapes.get(i).copy(
						shapes.get(i).getName() + "_rot_" + rounded);
				rotate.rotate(angleInRad);
				rotated.add(rotate);
				k++;
			}
			shapes.addAll(i + 1, rotated);
			i += k;
			rotated.clear();
		}
	}

	public static void drawShapes(List<TShape> shapes, String outputPath) {
		drawShapes(shapes, outputPath, Color.RED);
	}

	public static void drawShapes(List<TShape> shapes, String outputPath,
			Color color) {
		Debug.Image.setDebugFolder(outputPath);
		for (TShape shape : shapes) {
			Debug.Image.info(shape.getAsBufferedImage(color, 10), shape
					.getName());
		}
	}

	public static void writeShapesToXML(List<TShape> shapes, File xmlFile)
			throws IOException {
		if (null == shapes)
			throw new NullPointerException("Shape list is null.");
		if (null == xmlFile)
			throw new NullPointerException("XML file is null.");
		if (xmlFile.exists())
			throw new IOException("XML file already exists.");

		BufferedWriter bw = new BufferedWriter(new FileWriter(xmlFile));
		XStream xstream = new XStream();
		xstream.alias("tshape", TShape.class);
		xstream.useAttributeFor(TShape.class, "name");
		xstream.alias("ttriangle", TTriangle.class);
		xstream.alias("trectangle", TRectangle.class);
		xstream.alias("trtriangle", TRightTriangle.class);
		xstream.alias("tcircle", TCircle.class);
		xstream.alias("tgeneric", TGenericShape.class);
		xstream.alias("point2d", Point2d.class);
		xstream.alias("vector2d", Vector2d.class);
		xstream.alias("point2d", Point2d.class);
		xstream.useAttributeFor(Point2d.class, "x");
		xstream.useAttributeFor(Point2d.class, "y");
		xstream.setMode(XStream.NO_REFERENCES);
		xstream.toXML(shapes, bw);
		bw.close();
	}

	@SuppressWarnings("unchecked")
	public static List<TShape> readShapesFromXML(File xmlFile)
			throws IOException {
		if (null == xmlFile)
			throw new NullPointerException("XML file is null.");
		if (!xmlFile.exists())
			throw new IOException("XML file not exists.");

		BufferedReader br = new BufferedReader(new FileReader(xmlFile));
		XStream xstream = new XStream();
		xstream.alias("tshape", TShape.class);
		xstream.useAttributeFor(TShape.class, "name");
		xstream.alias("ttriangle", TTriangle.class);
		xstream.alias("trectangle", TRectangle.class);
		xstream.alias("trtriangle", TRightTriangle.class);
		xstream.alias("tcircle", TCircle.class);
		xstream.alias("tgeneric", TGenericShape.class);
		xstream.alias("point2d", Point2d.class);
		xstream.useAttributeFor(Point2d.class, "x");
		xstream.useAttributeFor(Point2d.class, "y");
		xstream.setMode(XStream.NO_REFERENCES);
		xstream.alias("vector2d", Vector2d.class);
		List<TShape> shapes = (ArrayList<TShape>) xstream.fromXML(br);

		return shapes;
	}

	public static void main(String[] args) {
		List<TShape> shapes = new ArrayList<TShape>();
		shapes.add(new TTriangle("tri_1", new Point2d(50, 50), new Point2d(50,
				100), new Point2d(200, 200)));
		try {
			TShapes.writeShapesToXML(shapes, new File("candidate_triangles.xml"));

			List<TShape> shapes2 = TShapes.readShapesFromXML(new File(
					"candidate.xml"));
			System.out.println(shapes2);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static List<TShape> readShapesFromTxt(File file) throws IOException {
		if (null == file)
			throw new NullPointerException("File is null.");
		if (!file.exists())
			throw new IOException("Cannot load file.");
		if (!file.canRead())
			throw new IOException("Cannot read file.");

		BufferedReader br = null;
		String line = null;
		List<TShape> shapes = new ArrayList<TShape>();
		try {
			br = new BufferedReader(new FileReader(file));
			while (null != (line = br.readLine())) {
				String[] data = line.split(";");
				if (data.length < MINIMAL_ARRAY_SIZE)
					System.err.println("Too short data array: "
							+ java.util.Arrays.toString(data));

				TShape shape = null;
				String type = data[0];
				String name = data[1];
				int paramStartIndex = 0;
				if (type.contentEquals(TCIRCLE)) {
					double radius = Double.parseDouble(data[2]);
					int parts = Integer.parseInt(data[3]);
					shape = new TCircle(name, radius, parts);
					paramStartIndex = 4;
				} else if (type.contentEquals(TRECTANGLE)) {
					double ax = Double.parseDouble(data[2]);
					double ay = Double.parseDouble(data[3]);

					double bx = Double.parseDouble(data[4]);
					double by = Double.parseDouble(data[5]);

					double cx = Double.parseDouble(data[6]);
					double cy = Double.parseDouble(data[7]);

					double dx = Double.parseDouble(data[8]);
					double dy = Double.parseDouble(data[9]);
					shape = new TRectangle(name, new Point2d(ax, ay),
							new Point2d(bx, by), new Point2d(cx, cy),
							new Point2d(dx, dy));
					paramStartIndex = 10;
				} else if (type.contentEquals(TRTRIANGLE)) {
					double ax = Double.parseDouble(data[2]);
					double ay = Double.parseDouble(data[3]);

					double width = Double.parseDouble(data[4]);
					double height = Double.parseDouble(data[5]);
					shape = new TRightTriangle(name, new Point2d(ax, ay),
							width, height);
					paramStartIndex = 6;
				} else if (type.contentEquals(TTRIANGLE)) {
					double ax = Double.parseDouble(data[2]);
					double ay = Double.parseDouble(data[3]);

					double bx = Double.parseDouble(data[4]);
					double by = Double.parseDouble(data[5]);

					double cx = Double.parseDouble(data[6]);
					double cy = Double.parseDouble(data[7]);
					shape = new TTriangle(name, new Point2d(ax, ay),
							new Point2d(bx, by), new Point2d(cx, cy));
					paramStartIndex = 8;
				} else if (type.contentEquals(TGENERIC)) {
					int count = Integer.parseInt(data[2]);
					int paramIndex = 3;
					List<Vector2d> segments = new ArrayList<Vector2d>();
					for (int k = 1; k <= count; k++) {
						double ax = Double.parseDouble(data[paramIndex]);
						double ay = Double.parseDouble(data[paramIndex + 1]);
						double bx = Double.parseDouble(data[paramIndex + 2]);
						double by = Double.parseDouble(data[paramIndex + 3]);
						Vector2d segment = new Vector2d(ax, ay, bx, by);
						segments.add(segment);
						paramIndex += 4;
					}

					shape = new TGenericShape(name, segments);
					paramStartIndex = paramIndex;
				} else {
					System.err.println("Unknown shape type: " + type);
					continue;
				}

				while (paramStartIndex < data.length) {
					String op = data[paramStartIndex];
					double param = Double
							.parseDouble(data[paramStartIndex + 1]);
					if (op.contentEquals(ROTATE))
						shape.rotate(param);
					else if (op.contentEquals(EXPLODE))
						shape.explode(param);
					else if (op.contentEquals(SCALE))
						shape.scale(param);
					else
						System.err.println("Unknown op type: " + op);
					paramStartIndex += 2;
				}

				shapes.add(shape);
			}
		} catch (Exception exc) {
			exc.printStackTrace();
		}

		return shapes;
	}

	public static List<TShape> readShape(File file) throws IOException {
		if (null == file)
			throw new NullPointerException("File is null.");
		if (!file.exists())
			throw new IOException("Cannot load file.");
		if (!file.canRead())
			throw new IOException("Cannot read file.");
		
		if(Utils.getExtension(file).equalsIgnoreCase(XML_EXT)) {
			return readShapesFromXML(file);
		} else if(Utils.getExtension(file).equalsIgnoreCase(TXT_EXT)) {
			return readShapesFromTxt(file);
		}
		
		return null;
	}
	
	public static final String XML_EXT = "xml";
	public static final String TXT_EXT = "txt";
	
	public static final int MINIMAL_ARRAY_SIZE = 4;
	public static final String TCIRCLE = "tcircle";
	public static final String TTRIANGLE = "ttriangle";
	public static final String TRECTANGLE = "trectangle";
	public static final String TRTRIANGLE = "trtriangle";
	public static final String TGENERIC = "tgeneric";
	public static final String ROTATE = "rotate";
	public static final String EXPLODE = "explode";
	public static final String SCALE = "scale";
}
